package oop.ex3.serverprotocol;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.LinkedList;
import java.util.TreeSet;

import oop.ex3.nameserver.NameServer;
import oop.ex3.sharedclasses.IPaddress;

public class Begin extends ClientMessageHandler {

	/*
	 * Will be used to temporarily store client's data while waiting for
	 * the whole process to complete,
	 * since the data should be entered after the whole process completes fine.
	 */
	private TreeSet<String> _clientFileTree;
	private LinkedList<IPaddress> _clientNameServers;
	
	/**
	 * Handles a Begin message.
	 * Will either send DONE if already knows this client,
	 * or respond with WELCOME and handles the welcome process.
	 * Also sets the sessionIP to the client's IP in case the begin process
	 * completed successfully.
	 */
	public boolean run(NameServer server, IPaddress sessionIP,
					DataInputStream clientInput, DataOutputStream clientOutput) {
		try {
			
			String ip=clientInput.readUTF();
			int port=clientInput.readInt();
			IPaddress clientIPaddress=new IPaddress(ip, port);
			
			if (endsWithEnd(clientInput)==FAILURE){ //No END
				return FAILURE;
			}
			
			if (server.knowsClient(clientIPaddress)){ //Knows this client, sends DONE
				
				if (ServerMessageSender.done(clientOutput)==FAILURE){
					return FAILURE;
				}
				
			} 
			
			else{ // Doesn't know client, sends WELCOME, waits for input
				if (ServerMessageSender.welcome(clientOutput)==FAILURE){
					return FAILURE;
				}
				
				if (handleWelcomeProcess(server, sessionIP, 
												clientInput, clientOutput)==FAILURE){
					return FAILURE;
				}
				
				//Welcome Process completed successfully
				//adding the new FileManager
				server.addNewClient(clientIPaddress, this._clientFileTree, 
															this._clientNameServers);
			}
			 // process almost complete - setting session IP.
			sessionIP.setIPAndPort(clientIPaddress.getIP(), clientIPaddress.getPort());
		} catch (IOException e) {
			return FAILURE;
		}
		return SUCCESS;
	}

	/**
	 * Handles the process that follows a WELCOME message.
	 * Will receive the file list & NameServer list of the client,
	 * and add them to the NameServer (in case of SUCCESS).
	 * @param server the NameServer to which the Message was sent.
	 * @param sessionIP the IP of the client in the session in which the message
	 * 				 	was sent.
	 * @param clientInput the Client's input stream.
	 * @param clientOutput the client's output stream.
	 * @return SUCCESS(true) if the process completed successfully,
	 * 				FAILURE(false) otherwise.
	 */
	private boolean handleWelcomeProcess(NameServer server,  IPaddress sessionIP,
					DataInputStream clientInput, DataOutputStream clientOutput) {
		
		// initiates the temporary storage for client's data
		this._clientFileTree=new TreeSet<String>();
		this._clientNameServers=new LinkedList<IPaddress>();
		
		//TODO check if allows NO FILES and NO NAME SERVERS. it should allow that,
		// and expect ENDLIST
		
		// handles ContainFile and ContainNameServer messages from client.
		if (handleWelcomeContainFile(server, sessionIP, 
												clientInput, clientOutput)==FAILURE){
			return FAILURE;
		}
		
		if (handleWelcomeContainNameServer(server, sessionIP,
											clientInput, clientOutput)==FAILURE){
			return FAILURE;
		}
		return SUCCESS;
	}

	/**
	 * Will handle the receiving of the WelcomeContainFile messages.
	 * the ContainFile messages sent by a client when creating a connection for the first time.
	 * @param server the NameServer to which the Message was sent.
	 * @param sessionIP the IP of the client in the session in which the message
	 * 				 	was sent.
	 * @param clientInput the Client's input stream.
	 * @param clientOutput the client's output stream.
	 * @return SUCCESS(true) if the process completed successfully,
	 * 				FAILURE(false) otherwise.
	 */
	private boolean handleWelcomeContainFile(NameServer server, IPaddress sessionIP, 
						DataInputStream clientInput,DataOutputStream clientOutput) {
		try {
			
			String inMessage;
			
			while (!(inMessage=clientInput.readUTF()).
											equals(ClientMessageHandler.END_LIST)){
				
				if (inMessage.equals(ClientMessageHandler.CONTAIN_FILE)){
					WelcomeContainFile containFile=new WelcomeContainFile();
					
					if (containFile.run(server, sessionIP, clientInput,
												clientOutput)==FAILURE){ //no END
						return FAILURE;
					}
					
					this._clientFileTree.add(containFile.getFileName());
					
					if(ServerMessageSender.done(clientOutput)==FAILURE){
						return FAILURE;
					}
				}
				else{
					//ILLEGAL MESSAGE - not containFile & not ENDLIST
					return FAILURE;
				}
			}
			
			if (endsWithEnd(clientInput)==FAILURE){ // no END after ENDLIST
				return FAILURE;
			}
			
			if (ServerMessageSender.done(clientOutput)==FAILURE){
				return FAILURE;
			}
			
		} catch (IOException e) {
			return FAILURE;
		}
		
		return SUCCESS;
	}
	
	/**
	 * Will handle the receiving of the WelcomeContainNameServer messages.
	 * the ContainNameServer messages sent by a client when creating a connection for the first time.
	 * @param server the NameServer to which the Message was sent.
	 * @param sessionIP the IP of the client in the session in which the message
	 * 				 	was sent.
	 * @param clientInput the Client's input stream.
	 * @param clientOutput the client's output stream.
	 * @return SUCCESS(true) if the process completed successfully,
	 * 				FAILURE(false) otherwise.
	 */
	private boolean handleWelcomeContainNameServer(NameServer server,  IPaddress sessionIP,
			DataInputStream clientInput, DataOutputStream clientOutput) {
		try {
			String inMessage;
			while (!(inMessage=clientInput.readUTF()).
											equals(ClientMessageHandler.END_LIST)){

				if (inMessage.equals(ClientMessageHandler.CONTAIN_NAME_SERVER)){
					WelcomeContainNameServer containNS=new WelcomeContainNameServer();
					if (containNS.run(server, sessionIP, clientInput, clientOutput)
							==FAILURE){
						// no END
						return FAILURE;
					}
					
					this._clientNameServers.add(containNS.getIPadress());
					
					if(ServerMessageSender.done(clientOutput)==FAILURE){
						return FAILURE;
					}
				}
				else{
					return FAILURE;
				}
			}
			if (!endsWithEnd(clientInput)){ // no END after ENDLIST
				return FAILURE;
			}
			
			ServerMessageSender.done(clientOutput);
			
		} catch (IOException e) {
			return FAILURE;
		}
		
		return SUCCESS;
		
	}

	
}
